package org.opengoofy.index12306.biz.ticketservice.canal;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import org.opengoofy.index12306.biz.ticketservice.common.enums.CanalExecuteStrategyMarkEnum;
import org.opengoofy.index12306.biz.ticketservice.mq.event.CanalBinlogEvent;
import org.opengoofy.index12306.framework.starter.bases.Singleton;
import org.opengoofy.index12306.framework.starter.cache.DistributedCache;
import org.opengoofy.index12306.framework.starter.designpattern.strategy.AbstractExecuteStrategy;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import java.util.*;

import static org.opengoofy.index12306.biz.ticketservice.common.constant.RedisKeyConstant.TRAIN_STATION_REMAINING_TICKET;

/**
 * 列车余票缓存更新组件
 */
@Component
@RequiredArgsConstructor
public class TicketAvailabilityCacheUpdateHandler implements AbstractExecuteStrategy<CanalBinlogEvent, Void> {

    private static final String TICKET_REMAIN_NUMBER_PATH = "lua/ticket_remain_number.lua";
    private final DistributedCache distributedCache;

    @Override
    public void execute(CanalBinlogEvent message) {
        // 快速过滤无效数据并构建变更映射
        Map<String, Map<Integer, Integer>> cacheChangeKeyMap = buildCacheChangeMap(message);

        // 如果没有需要处理的变更，直接返回
        if (cacheChangeKeyMap.isEmpty()) {
            return;
        }

        // 执行Redis批量操作
        executeRedisScript(cacheChangeKeyMap);
    }

    /**
     * 构建缓存变更映射，在一次循环中完成数据过滤和统计
     */
    private Map<String, Map<Integer, Integer>> buildCacheChangeMap(CanalBinlogEvent message) {
        Map<String, Map<Integer, Integer>> cacheChangeKeyMap = new HashMap<>();

        // 合并处理新旧数据
        List<Map<String, Object>> dataList = message.getData();
        List<Map<String, Object>> oldList = message.getOld();

        for (int i = 0; i < dataList.size(); i++) {
            Map<String, Object> oldData = oldList.get(i);
            Map<String, Object> newData = dataList.get(i);

            // 快速筛选有效变更（座位状态从已售变为可用，或从可用变为已售）
            Object oldStatusObj = oldData.get("seat_status");
            Object newStatusObj = newData.get("seat_status");

            if (oldStatusObj == null || newStatusObj == null) {
                continue;
            }

            String oldStatus = oldStatusObj.toString();
            String newStatus = newStatusObj.toString();

            // 仅处理座位状态变化的情况（已售<->可用）
            if (!StrUtil.equals(oldStatus, newStatus) &&
                    (StrUtil.equalsAny(oldStatus, "0", "1") || StrUtil.equalsAny(newStatus, "0", "1"))) {

                // 计算增量：从已售(0)变为可用(1)则+1，反之-1
                int increment = StrUtil.equals(oldStatus, "0") ? 1 : -1;

                // 构建缓存键
                String trainId = newData.get("train_id").toString();
                String startStation = newData.get("start_station").toString();
                String endStation = newData.get("end_station").toString();
                String cacheKey = TRAIN_STATION_REMAINING_TICKET + trainId + "_" + startStation + "_" + endStation;

                // 座位类型
                Integer seatType = Integer.parseInt(newData.get("seat_type").toString());

                // 更新缓存变更映射
                cacheChangeKeyMap.computeIfAbsent(cacheKey, k -> new HashMap<>())
                        .merge(seatType, increment, Integer::sum);
            }
        }

        return cacheChangeKeyMap;
    }

    /**
     * 执行Redis Lua脚本批量更新余票
     */
    private void executeRedisScript(Map<String, Map<Integer, Integer>> cacheChangeKeyMap) {
        StringRedisTemplate redisTemplate = (StringRedisTemplate) distributedCache.getInstance();

        // 获取Lua脚本（使用单例缓存）
        DefaultRedisScript<String> redisScript = Singleton.get(TICKET_REMAIN_NUMBER_PATH, () -> {
            DefaultRedisScript<String> script = new DefaultRedisScript<>();
            script.setScriptSource(new ResourceScriptSource(new ClassPathResource(TICKET_REMAIN_NUMBER_PATH)));
            script.setResultType(String.class);
            return script;
        });

        // 准备脚本参数
        List<String> keys = new ArrayList<>(cacheChangeKeyMap.keySet());
        List<String> args = new ArrayList<>();

        // 构建参数字符串
        for (Map.Entry<String, Map<Integer, Integer>> entry : cacheChangeKeyMap.entrySet()) {
            Map<Integer, Integer> seatTypeMap = entry.getValue();

            // 添加座位类型数量
            args.add(String.valueOf(seatTypeMap.size()));

            // 添加每个座位类型的增量
            for (Map.Entry<Integer, Integer> seatEntry : seatTypeMap.entrySet()) {
                args.add(String.valueOf(seatEntry.getKey()));      // 座位类型
                args.add(String.valueOf(seatEntry.getValue()));   // 增量值
            }
        }

        // 执行Lua脚本
        redisTemplate.execute(redisScript, keys, args.toArray(new String[0]));
    }

    @Override
    public String mark() {
        return CanalExecuteStrategyMarkEnum.T_SEAT.getActualTable();
    }
}